/*
 * Indexador.cpp
 *
 *  Created on: 22/08/2011
 *      Author: leonel
 */

#include "Indexador.h"

Indexador::Indexador(){
	//Creo un archivo para guardar las rutas de los documentos indexados
	this->archivoRutaDeDocumentos = new ArchivoRuta("archivoDeRutasDeDocumentosIndexados");
	this->indiceInvertido = new IndiceInvertido();
}

void Indexador::agregarAIndiceInvertido(RegistroPalabra unRegistroPalabra, int unId){

	bool seEncuentra = this->indiceInvertido->esta(unRegistroPalabra.getPalabra());

	RegistroDePesosNormalizados* unRegistroDePesosNormalizados = new RegistroDePesosNormalizados(unId,unRegistroPalabra.getPeso());

	//Si el termino ya se encuentra en el indice invertido
	if(seEncuentra){
		RegistroIndice* unRegistroIndice = this->indiceInvertido->obtenerRegistro(unRegistroPalabra.getPalabra());
		unRegistroIndice->agregarDocumento(*unRegistroDePesosNormalizados);
	}

	//Si el termino no se encuentra en el indice invertido
	else{
		RegistroIndice* unRegistroIndice = new RegistroIndice();
		unRegistroIndice->agregarDocumento(*unRegistroDePesosNormalizados);

		//Agrego el nuevo registro a lista invertida
		this->indiceInvertido->agregarRegistro(unRegistroPalabra.getPalabra(), unRegistroIndice);
	}
}

void Indexador::indexar(list<listaDeRegPalabras>* listaDeTerminos, list<string>* listaDePaths){


	//Cada elemento de la lista contiene una lista con todos los terminos correspondiente a un documento
	list<listaDeRegPalabras>::iterator iteradorDeListaDeTerminosPorDocumento =  listaDeTerminos->begin();

	//Cada elemento de la lista contiene el path de un documento en el mismo orden que la lista anterior
	list<string>::iterator iteradorRuta = listaDePaths->begin();

	//seteo cantidad de documentos
	int archivosTotales = listaDePaths->size();

	//Me fijo si esta abierto el ArchivoRutas
	this->archivoRutaDeDocumentos->abrirParaEscribir();

	this->archivoRutaDeDocumentos->escribirCantidadTotal(archivosTotales);

	while(iteradorDeListaDeTerminosPorDocumento != listaDeTerminos->end()){
		//Tomo una lista de todos los terminos de un documento
		listaDeRegPalabras unaListaDeterminos = *iteradorDeListaDeTerminosPorDocumento;

		//Inicializo el iterador
		listaDeRegPalabras::iterator iteradorTerminos = unaListaDeterminos.begin();

		//Tomo el path correspondiente al documento y lo guardo en el archivo de rutas
		string unaRuta = *iteradorRuta;

		//Creo un registro ruta
		RegistroRuta* unRegistroRuta = new RegistroRuta(unaRuta);

		//Lo guardo en el archivo de rutas
		int id = this->archivoRutaDeDocumentos->escribir(unRegistroRuta);

		while(iteradorTerminos != unaListaDeterminos.end()){
			RegistroPalabra unRegistroPalabra = *iteradorTerminos;
			agregarAIndiceInvertido(unRegistroPalabra, id);
			iteradorTerminos++;
		}

		iteradorRuta++;
		iteradorDeListaDeTerminosPorDocumento++;
	}

	this->archivoRutaDeDocumentos->marcarFin();
	this->archivoRutaDeDocumentos->cerrar();

	this->almacenar();
}

//reindexa truncando los archivos
void Indexador::reindexar(string nuevoDir){
	this->archivoRutaDeDocumentos->sobreescribir();
	this->indiceInvertido->destruir();

	PreIndexador *preIndexer = new PreIndexador();
	list<string>* homeFiles = new list<string>();
	homeFiles = preIndexer->preIndexarPaths(nuevoDir);
	this->indexar(preIndexer->preIndexarTerminos(homeFiles), homeFiles);

}

//metodo que agrega mas directorios, y mantiene los que ya estaban
void Indexador::agragarDirectorio(string pathNuevoDir){
	//pido los paths de los documentos que ya estaban indexados
	list<string> pathsAnteriores = this->salvarPaths();

	this->archivoRutaDeDocumentos->sobreescribir();
	this->indiceInvertido->destruir();
	PreIndexador *preIndexer = new PreIndexador();
	list<string>* homeFiles = new list<string>();
	homeFiles = preIndexer->preIndexarPaths(pathNuevoDir);



	//vuelvo a incorporar a la lista de paths, los paths anteriores
	list<string>::iterator iterPathsAnteriores = pathsAnteriores.begin();
	while(iterPathsAnteriores != pathsAnteriores.end()){
		homeFiles->push_back(*iterPathsAnteriores);
		iterPathsAnteriores++;

	}

	this->indexar(preIndexer->preIndexarTerminos(homeFiles), homeFiles);

}

//metodo que devuelve una lista con paths de los directorios que estaban indexados
list<string> Indexador::salvarPaths(){
	list<string> listaPaths;
	RegistroRuta *unRegRuta = new RegistroRuta();
	this->archivoRutaDeDocumentos->abrirParaLeer();
	unRegRuta = this->archivoRutaDeDocumentos->leer(4);
	while(unRegRuta != NULL){
		listaPaths.push_back(unRegRuta->obtenerRuta());
		unRegRuta = this->archivoRutaDeDocumentos->leerSiguiente();
	}
	this->archivoRutaDeDocumentos->cerrar();
	return listaPaths;

}

/*metodo que se encarga de almacenar la informacion que esta en el arbol, en disco.
 *
 */
void Indexador::almacenar(){
	this->indiceInvertido->almacenar();
}


/*metodo que calcula la relevancia de cada documento relacionado con la consulta y devuelve una lista de
 * RegistroRelevanciaDeDocumento donde cada registro esta compuesto por un ID_doc y su relevancia en la consulta
 */
vector<RegistroRelevanciaDeDocumento> Indexador::obtenerRelevanciasDeDocumentosAsociadosConLaConsulta(list<string> &listaConsulta, int cantTotalArch){

	//primero me armo la lista de los registros Indice que estan involucrados en la consulta
	list<RegistroIndice> listaDeRegIndicesDeLaConsulta;
	bool algunTernimoEsRelevante = false; //puede que ningun termino de la consulta este en el indice
	list<string>::iterator it_term = listaConsulta.begin();

	for(it_term = listaConsulta.begin();it_term != listaConsulta.end();it_term++){
		//tomo un termino y lo busco en la arbol
		RegistroIndice *ri_control = this->buscar((*it_term));
		if(ri_control != NULL){//si encontre el registro que tiene el termino
			algunTernimoEsRelevante = true;
			// y lo agrego a la listaDeRegIndiceDeLaConsulta
			listaDeRegIndicesDeLaConsulta.push_back(*ri_control);
		}
	}

	//me creo una lista de registrosRelevanciaDeDoc
	vector<RegistroRelevanciaDeDocumento> vectorDeRelevanciasDeDoc;

	//ahora ya tengo una lista donde solo estan los regIndices involucrados en la consulta
	//nuevamente me pongo al principio de la lista de terminos de la consulta
	if(algunTernimoEsRelevante){
//		cout<<"hay doc relevantes"<<endl;

		// me creo un objeto utilidad que voy a usar porque voy a usar una lista que no es la lista invertida
		Utilidad *uti = new Utilidad();

		//iterador para iterar sobre los regIndice de la consulta
	 list<RegistroIndice>::iterator it_regIndicesDeLaConsulta;
	 for(it_regIndicesDeLaConsulta = listaDeRegIndicesDeLaConsulta.begin();it_regIndicesDeLaConsulta != listaDeRegIndicesDeLaConsulta.end();it_regIndicesDeLaConsulta++){
         //tomo un registro indice para trabajar con sus documentosRelacionados
		 RegistroIndice ri = (*it_regIndicesDeLaConsulta);

			//para cada uno de estos ID tengo que hacer el calculo
			 list<RegistroIndice>::iterator it_regIndicesDeLaConsulta2;

			 int tam = ri.obtenerDocumentosAsociados().size();
			 for(int i = 0;i < tam; i++){
				//para cada doc creo un RegistroRelevanciaDeDoc y una similiridad
				//pero antes veo si ya esta en la lista de RelevanciasDeDoc

				 //control
				 int doc = ri.obtenerDocumentosAsociados()[i].get_ID_arch();
				bool yaEsta = uti->estaEnListaDeRelevancias(vectorDeRelevanciasDeDoc, doc);

				if(!yaEsta){ //osea si no esta le hago el calculo

				 RegistroRelevanciaDeDocumento *regRelevancia = new RegistroRelevanciaDeDocumento(doc);
				 float similiridad = 0.0;

				 //ahora entro en un for sobre los regIndicesDeLaConsulta
				 for(it_regIndicesDeLaConsulta2 = listaDeRegIndicesDeLaConsulta.begin();it_regIndicesDeLaConsulta2 != listaDeRegIndicesDeLaConsulta.end();it_regIndicesDeLaConsulta2++){
					 RegistroIndice ri2 = (*it_regIndicesDeLaConsulta2);

					 //busco id_doc
					 RegistroDePesosNormalizados regDePesos = ri2.obtenerDocumentoConID(regRelevancia->getId_Doc());
//					 if(regDePesos.get_ID_arch() != 0 && (regDePesos.getPeso() != 0.0)){
					 if(regDePesos.getPeso() != 0.0){
						 //entonces quiere decir que estoy en un regIndice que contiene el ID_doc
						similiridad = similiridad + (regDePesos.getPeso() * (float)log10((float)cantTotalArch / (float)ri2.obtenerDocumentosAsociados().size()));

					 }
				 }

				 regRelevancia->setRelevancia(similiridad);
				 //una ves recorrida la lista de RegistroIndice agrego el RegistroRelevancia a la lista de RelevanciasDeDoc
				 vectorDeRelevanciasDeDoc.push_back(*regRelevancia);
			    } // del if que controlo si el id_doc ya esta en la lista
			}
	 }

	 uti->ordenarPorShellSort(vectorDeRelevanciasDeDoc);
	} //este es del if que controla si hay algun termino de la consulta en el indice
	return (vectorDeRelevanciasDeDoc);
}


Indexador::~Indexador(){
	delete this->archivoRutaDeDocumentos;
	delete this->indiceInvertido;
}

/*el metodo esta y el metodo obtener registro hacen lo mismo, entonces se pierde tiempo haciendo una
 * misma busqueda dos veces (sacar una de las 2)
 */
RegistroIndice* Indexador::buscar(string termino){
	if(this->indiceInvertido->esta(termino))
		return this->indiceInvertido->obtenerRegistroIndice(termino);
	else
		return NULL;
}

//se busca la consulta ingresada por el usuario
list<string> Indexador::buscarConsulta(string consulta){
	this->indiceInvertido->cargar();
	Parser *parser = new Parser();
	this->archivoRutaDeDocumentos->abrirParaLeer();
	int canArchTotales = this->archivoRutaDeDocumentos->leerCantidadTotal();
	vector<RegistroRelevanciaDeDocumento> vecResult = this->obtenerRelevanciasDeDocumentosAsociadosConLaConsulta(*(parser->obtenerListaDeConsulta(consulta)),canArchTotales);
	list<string> pilaDeDocumentosRelevantes;

	for(int i = 0;i < vecResult.size(); i++){
		/* acceso al archivo de rutas para obtener la ruta de los doc relevantes */
		pilaDeDocumentosRelevantes.push_front((this->archivoRutaDeDocumentos->leer(vecResult[i].getId_Doc()))->obtenerRuta());
	}
	this->archivoRutaDeDocumentos->cerrar();
	return pilaDeDocumentosRelevantes;

}

